package org.mx.service.server;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;

import org.apache.http.HttpVersion;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
import org.mx.ClassUtils;
import org.mx.StringUtils;
import org.mx.error.UserInterfaceSystemErrorException;
import org.mx.service.server.jmx.HttpServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;

/**
 * 描述： 基于Jetty的Http服务的工程类
 *
 * @author John.Peng Date time 2018/3/11 上午11:11
 */
public abstract class HttpServerFactory extends AbstractServerFactory {
	private static final Logger logger = LoggerFactory.getLogger(HttpServerFactory.class);

	private HttpServerConfigBean httpServerConfigBean;
	/**
	 * Spring IoC上下文
	 */
	protected ApplicationContext context;
	/**
	 * HttpServerMXBean对象，可能是：WebServerMXBean, WebsocketServerMXBean, RestfulServerMXBean ...
	 */
	protected HttpServer httpServerMXBean;

	/**
	 * 构造函数
	 * 
	 * @param context              Spring IoC上下文
	 * @param httpServerConfigBean HTTP服务器配置信息
	 * @param httpServerMXBean	HTTP服务器MXBean对象
	 */
	public HttpServerFactory(ApplicationContext context, HttpServerConfigBean httpServerConfigBean,
			HttpServer httpServerMXBean) {
		super();
		this.context = context;
		this.httpServerConfigBean = httpServerConfigBean;
		this.httpServerMXBean = httpServerMXBean;
	}

	/**
	 * 获取本Server特定的Handler
	 *
	 * @return Handler
	 */
	protected abstract Handler getHandler();

	/**
	 * {@inheritDoc}
	 *
	 * @see AbstractServerFactory#init()
	 */
	public void init() throws Exception {
		if (!httpServerConfigBean.isEnabled()) {
			// 显式配置enable为false，表示不进行初始化。
			return;
		}

		Handler handler = getHandler();
		if (handler == null) {
			if (logger.isWarnEnabled()) {
				logger.warn("There are not a handler, the server can't start.");
			}
		} else {
			QueuedThreadPool threadPool = new QueuedThreadPool(httpServerConfigBean.getThreads());
			Server server = new Server(threadPool);
			server.addBean(new ScheduledExecutorScheduler());
			server.setHandler(handler);
			HttpConfiguration httpConfiguration = new HttpConfiguration();
			httpConfiguration.setOutputBufferSize((int) httpServerConfigBean.getOutputSize());
			httpConfiguration.setRequestHeaderSize((int) httpServerConfigBean.getRequestHeaderSize());
			httpConfiguration.setResponseHeaderSize((int) httpServerConfigBean.getResponseHeaderSize());
			httpConfiguration.setSendServerVersion(true);
			httpConfiguration.setSendDateHeader(false);
			if (httpServerConfigBean.isSecurity()) {
				// 创建HTTPS
				httpConfiguration.setSecureScheme("HTTPS");
				httpConfiguration.setSecurePort(httpServerConfigBean.getPort());
				SecureRequestCustomizer src = new SecureRequestCustomizer();
				src.setStsMaxAge(2000);
				src.setStsIncludeSubDomains(true);
				httpConfiguration.addCustomizer(src);

				SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
				sslContextFactory.setValidateCerts(httpServerConfigBean.isValidateCerts());
				sslContextFactory.setValidatePeerCerts(httpServerConfigBean.isValidatePeerCerts());
				sslContextFactory.setNeedClientAuth(httpServerConfigBean.isNeedClientAuth());
				sslContextFactory.setIncludeCipherSuites(httpServerConfigBean.getCipherSuites());
				sslContextFactory.setIncludeProtocols(httpServerConfigBean.getProtocols());
				sslContextFactory.setKeyManagerFactoryAlgorithm(httpServerConfigBean.getKeyManagerFactoryAlgorithm());
				sslContextFactory.setMaxCertPathLength(httpServerConfigBean.getMaxCertPathLength());
				sslContextFactory.setWantClientAuth(httpServerConfigBean.isWantClientAuth());

				sslContextFactory.setKeyStoreType(httpServerConfigBean.getKeystoreType());
				sslContextFactory.setKeyStorePath(httpServerConfigBean.getKeystorePath());
				sslContextFactory.setTrustStorePath(httpServerConfigBean.getKeystorePath());
				sslContextFactory.setKeyStorePassword(httpServerConfigBean.getKeystorePassword());
				sslContextFactory.setKeyManagerPassword(httpServerConfigBean.getKeyManagerPassword());
				sslContextFactory.setTrustStorePassword(httpServerConfigBean.getKeystorePassword());
				sslContextFactory.setCertAlias(httpServerConfigBean.getKeyAlias());

				ServerConnector https = new ServerConnector(server,
						new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.toString()),
						new HttpConnectionFactory(httpConfiguration));
				https.setHost(httpServerConfigBean.getHost());
				https.setPort(httpServerConfigBean.getPort());
				https.setIdleTimeout(httpServerConfigBean.getIdleTimeoutSecs() * 1000);
				server.addConnector(https);
			} else {
				// 创建HTTP
				ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(httpConfiguration));
				http.setHost(httpServerConfigBean.getHost());
				http.setPort(httpServerConfigBean.getPort());
				http.setIdleTimeout(httpServerConfigBean.getIdleTimeoutSecs() * 1000);
				server.addConnector(http);
			}
			server.setDumpAfterStart(false);
			server.setDumpBeforeStop(false);
			server.setStopAtShutdown(true);
			server.setStopTimeout(10);
			server.start();
			super.setServer(server);
			if (logger.isInfoEnabled()) {
				logger.info(String.format("Start Server[%s] success, listen : %d, security: %s.",
						httpServerConfigBean.getServerType(), httpServerConfigBean.getPort(),
						httpServerConfigBean.isSecurity()));
			}
			if (httpServerMXBean != null) {
				httpServerMXBean.serverStarted();
			}
		}
	}

	/**
	 * 获取HTTP服务器加载的资源
	 * 
	 * @param classes           类名数组
	 * @param basePackages      基准包数组
	 * @param basePackageTester 扫描执行器
	 * @return 资源数组
	 */
	@SuppressWarnings("unchecked")
	protected List<Class<?>> getResourceClasses(String[] classes, String[] basePackages,
			Predicate<Class<?>> basePackageTester) {
		List<Class<?>> resourceClasses = new ArrayList<>();
		// 从配置的RestResourceClasses中获取Rest资源类
		if (classes == null || classes.length <= 0) {
			if (logger.isWarnEnabled()) {
				logger.warn("You not define any service resource.");
			}
		} else {
			for (String classesDef : classes) {
				if (!StringUtils.isBlank(classesDef)) {
					List<Class<?>> list = (List<Class<?>>) context.getBean(classesDef, List.class);
					if (!list.isEmpty()) {
						resourceClasses.addAll(list);
					}
				}
			}
		}
		// 从配置的包中自动扫描Rest资源类
		Set<String> basePackageSet = new HashSet<String>();
		if (!StringUtils.isBlank(httpServerConfigBean.getSpringBootClassPackage())) {
			// 如果已经配置了`spring.rest.class.package`，则将其自动加入到搜索路径中
			basePackageSet.add(httpServerConfigBean.getSpringBootClassPackage());
		}
		for (String base : basePackages) {
			basePackageSet.add(base);
		}
		if (basePackageSet.isEmpty()) {
			if (logger.isWarnEnabled()) {
				logger.warn("You not define any resource base package.");
			}
		} else {
			for (String basePackage : basePackageSet) {
				if (!StringUtils.isBlank(basePackage)) {
					List<String> classNames = ClassUtils.scanPackage(basePackage);
					for (String name : classNames) {
						try {
							Class<?> clazz = Class.forName(name);
							if (basePackageTester.test(clazz)) {
								resourceClasses.add(clazz);
							}
						} catch (ClassNotFoundException ex) {
							if (logger.isErrorEnabled()) {
								logger.error(String.format("The class[%s] not found.", name), ex);
							}
							throw new UserInterfaceSystemErrorException(
									UserInterfaceSystemErrorException.SystemErrors.CLASS_NOT_FOUND);
						}
					}
				}
			}
		}
		return resourceClasses;
	}
}
